package cluster

import (
	"fmt"
	"sync"
	"time"

	"github.com/sirupsen/logrus"

	"github.com/spf13/cast"

	"gitee.com/zfd81/dbrs/config"

	"github.com/hashicorp/memberlist"
	"github.com/zfd81/rooster/types/container"
)

const (
	Content_Separator = ":"
)

var (
	conf       = config.GetConfig()
	self       Member
	leader     = &Leader{}
	members    = map[string]Member{}
	broadcasts *memberlist.TransmitLimitedQueue
	mtx        sync.RWMutex
)

func Register() error {
	c := memberlist.DefaultLocalConfig()
	c.Events = &event{}
	c.Delegate = &delegate{}
	c.BindPort = int(conf.Cluster.Port)
	c.Name = fmt.Sprintf("%d%s%d%s%d", time.Now().Unix(), Content_Separator, conf.Port, Content_Separator, conf.ControlPort)
	cluster, err := memberlist.Create(c)
	if err != nil {
		return fmt.Errorf("Failed to create memberlist: " + err.Error())
	}
	self = Member(*cluster.LocalNode())
	set := make(container.HashSet)
	set.Add(fmt.Sprintf("%s:%d", self.Addr.String(), self.Port)) //添加自身真实地址
	for _, m := range conf.Cluster.Members {
		set.Add(m) //添加成员列表
	}
	set.Remove(fmt.Sprintf("%s:%d", "127.0.0.1", self.Port)) //移除自身本机地址
	set.Remove(fmt.Sprintf("%s:%d", "localhost", self.Port)) //移除自身本机地址

	//构建成员列表
	addresses := make([]string, 0)
	set.Iterator(func(index int, value interface{}) {
		addresses = append(addresses, cast.ToString(value))
	})

	//Join an existing cluster by specifying known members.
	_, err = cluster.Join(addresses)
	if err != nil {
		return fmt.Errorf("Failed to join cluster: " + err.Error())
	}

	broadcasts = &memberlist.TransmitLimitedQueue{
		NumNodes: func() int {
			return cluster.NumMembers()
		},
		RetransmitMult: 3, // 最大重新传输的次数
	}

	logrus.Info("Cluster members list: ")
	mtx.Lock()
	for _, member := range cluster.Members() {
		m := Member(*member)
		members[member.Name] = m
		logrus.Infof(" - Member: %s \n", m.ServAddress())
	}
	mtx.Unlock()
	if len(members) == 1 {
		leader.BeElected()
		logrus.Infof("%s elected leader", Self().ServAddress())
	}
	return nil
}

func Self() *Member {
	return &self
}

func IsLeader() bool {
	if leader.Id == "" {
		return false
	}
	if lnode := LeaderMember(); lnode != nil {
		return self.Name == lnode.Name
	}
	return false
}

func LeaderMember() *Member {
	member, found := members[leader.Id]
	if found {
		return &member
	}
	return nil
}

func Members() map[string]Member {
	return members
}

type Leader struct {
	Id        string
	TimeStamp int64
}

func (l *Leader) BeElected() *Leader {
	l.Id = Self().Name
	l.TimeStamp = time.Now().Unix()
	return l
}
func (l *Leader) LostElected() {
	l.Id = ""
	l.TimeStamp = time.Now().Unix()
}
